JPA(Java Persistence API)
✒️ 2025-06-30 02:25 내용 수정
스프링부트3 자바 백엔드 개발입문 내용 참고 및 정리
Java에서 객체와 DB 데이터를 매핑 시켜주는 ORM 기술을 제공하는 도구
- 참고 자료 : 골든래빗's 스프링데이터 JPA, 5분 만에 알아보기
- 데이터 영속성(Data Persistence) : 애플리케이션이 종료 되어도 데이터가 사라지지 않고 유지되는 것을 의미한다.
- 데이터베이스, 클라우드 시스템, 파일 시스템 등으로 데이터를 저장하는 방법으로 구현된다.
- MyBatis와 JPA 비교 : 개발 테크's JPA vs Mybatis, 현직 개발자는 이럴 때 사용합니다.
- MyBatis는 SQL 실행 결과를 객체에 매핑 시켜주는 SQL Mapper이며, JPA는 ORM을 제공한다.
- MyBatis는 SQL문을 직접 작성하기 때문에 복잡한 query와 SQL 제어가 필요한 경우 사용하기 좋으며, 간단한 매핑 및 객체 지향적 접근이 필요하다면 JPA를 사용하기 좋다.
- 성능적 측면이 중요하다면 JPA같은 ORM 사용보다 SQL문을 직접 작성하는 것이 좋다.
- JPA는 Hibernate ORM 프레임워크를 사용하며, Hibernate는 JDBC API를 내부적으로 사용한다.
엔티티(Entity)
- 데이터베이스의 테이블과 매핑되는 객체다.
- 본질적으론 Java의 객체이나, DB 테이블과 직접 연결되는 점이 특징이기에 이를 구분 지어 부른다.
- Entity 선언은
@EntityAnnotation을 사용한다. - Entity와 연결될 데이터베이스의 테이블은
@Table로 지정할 수 있다.- 이름을 기준으로 데이터베이스에 Entity 이름과 같은 테이블이 존재하면 해당 테이블에 Mapping이 되고, 없는 경우엔 테이블을 새로 생성한다.
- Entity의 상세 설정은 JPA Entity 설정하기 참고.
package com.example.demo.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import lombok.Data;
@Entity // Entity임을 명시하는 Annotation
@Data // Lombok
public class Article {
@Id // 식별자
@GeneratedValue
private Long id;
@Column // DB에서 열과 대응되는 속성
private String title;
@Column
private String content;
}
엔티티 연관 개념과 특징
1) 엔티티 매니저(Entity Manager)
- 엔티티를 관리해 DB와 어플리케이션 사이에서 객체를 생성, 수정, 삭제한다.
- 엔티티 매니저는 Spring Data JPA에서 관리하기에 직접 관리할 필요가 없다.
- 엔티티 매니저는 엔티티를 관리하는 가상 공간인 영속성 컨텍스트에 엔티티를 저장한다.
- 데이터베이스의 트랜잭션도 담당하여 데이터의 일관성과 안정성을 보장한다.
2. 엔티티 매니저 팩토리(Entity Manager Factory)
- 엔티티 매니저를 만드는 곳이다.
3. 영속성 컨텍스트(Persistence Context)
- 엔티티 매니저가 관리하며, 엔티티를 저장하는 가상 공간이다.
- 데이터베이스와 바로 연결되는 것이 아닌 컨텍스트 내에서 작업이 이루어지기 때문에 데이터베이스에 불필요하게 자주 접근하지 않는다.
- 1차 캐시 : 컨텍스트에 엔티티 객체를 메모리에 저장한다.
- 캐시의 키는 엔티티의
@IdAnnotation이 달린 기본키 역할을 하는 식별자이고, 값은 엔티티이다. - snapshot 방식으로 변경 감지 단계에서 엔티티의 상태를 비교할 때에도 사용한다.
- 캐시의 키는 엔티티의
- 쓰기 지연(Transaction write-behind) : transaction을 commit하기 전까지 DB에 실제 쿼리를 보내지 않고 모았다가, transaction을 commit할 때 모아둔 query를 한 번에 실행한다.
- 이를 통해 DB 시스템의 부담을 줄일 수 있다.
- 변경 감지(Dirty Checking) : transaction commit 시 1차 캐시에 저장된 엔티티의 값과 현재 엔티티의 값을 비교해 변경 사항이 있으면 그 값을 DB에 자동으로 반영한다.
- 개발자가 직접 명시적으로 업데이트 query를 작성하지 않아도 객체에 변경 사항이 발생하면 transaction commit 시에 자동으로 적용된다.
- 지연 로딩(Lazy-loading) : 필요할 때 query를 날려 데이터를 조회한다.
엔티티의 상태
-
분리 상태(detached) : 엔티티를 처음 만들거나
detach()메소드를 사용하면 가지는 상태로, 영속성 컨텍스트가 관리하고 있지 않은 상태이다.- 객체는 여전히 존재하나 영속성 컨텍스트가 객체의 상태를 추적하지 않는 상태다.
-
관리 상태(managed) :
persist()메소드를 사용하면 가지는 상태로, 영속성 컨텍스트가 관리하는 상태이다.- 관리 상태일 때 변경 사항이 생기면 상태를 자동 추적하는 변경 감지가 일어난다.
-
비영속 상태(transient) : 영속성 컨텍스트에 등록되지 않은 상태다.
- 엔티티 매니저가 관리하지 않는 객체이며, 데이터베이스와 연결되어 있지 않다.
-
삭제된 상태(removed) :
remove()메소드를 사용하면 가지는 상태로, 엔티티가 영속성 컨텍스트와 DB에서 삭제된다.- 삭제 상태의 트랜잭션이 commit되면 데이터베이스에서도 제거된다.
리파지토리(Repository)
- 참고 자료 : 점프 투 스프링부트 리포지터리로 데이터베이스 관리하기, Spring Defining Repository Interfaces
- 엔티티에 대한 CRUD(Create, Read, Update, Delete) 작업을 수행하기 위한 인터페이스이다.
- 반복적인 CRUD 작업을 위한 코드를 작성하지 않고 쉽게 DB 작업을 수행할 수 있도록 한다.
- Repository 생성 예시로는 Custom Repository interface를 생성할 때
CrudRepository를 상속 받아 생성하며,CrudRepository에는 CRUD 작업을 처리하는 메서드들이 내장되어 있다.
| interface | 설명 |
|---|---|
Repository |
최상위 Repository interface |
CrudRepository |
Entity의 CRUD 기능 제공 |
ListCrudRepository |
Entity의 CRUD 기능 제공 |
PagingAndSortingRepository |
Entity의 Paging 및 정렬 기능 제공 |
ListPagingAndSortingRepository |
Entity의 Paging 및 정렬 기능 제공 |
JpaRepository |
Entity의 CRUD, Paging, 정렬, JPA 특화된 기능 제공 |
import org.springframework.data.jpa.repository.CrudRepository;
public interface CustomRepository extends CrudRepository<CustomClass, ID> {
// CustomClass 부분엔 관리 대상 엔티티의 클래스 타입을 작성
// ID 부분엔 관리 대상 엔티티의 대푯값 타입을 작성하며,
// 대상 엔티티의 @Id 식별자에 @GeneratedValue로 지정한 값의 타입을 작성한다
// 추가적인 쿼리 메소드 정의 가능
}
메서드 목록
- Repository에는 SQL Query를 실행하는 메서드가 있으며, 기본 제공되는 메서드를 사용하거나 직접 메서드를 생성해서 query를 수행할 수 있다.
- 메서드 목록은
CrudRepository에서 제공하는 메서드 일부를 정리했다.CrudRepository를 상속 받는 다른 인터페이스에서도 동일하게 사용할 수 있다.
- 참고 자료 : Spring JPA Query Method, Spring docs Interface JpaRepository, Spring docs Interface CrudRepository
| 메서드 | 설명 |
|---|---|
findAll() |
모든 Entity 조회 |
findById(Id id) |
Id로 Entity 조회 |
save(T entity) |
Entity를 저장 |
saveAll(Iterable<T> entities) |
Entity들을 저장 |
count() |
사용 가능한 Entity 수 반환 |
existsbyId(Id id) |
id로 Entity 존재 여부 확인 |
delete(T entity) |
Entity를 제거 |
deleteById(Id id) |
지정한 Id를 가진 Entity 제거 |
deleteAll(Interable<T> entities) |
Entity들을 모두 제거 |
// CrudRepository
package org.springframework.data.repository;
import java.util.Optional;
@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {
<S extends T> S save(S entity);
<S extends T> Iterable<S> saveAll(Iterable<S> entities);
Optional<T> findById(ID id);
boolean existsById(ID id);
Iterable<T> findAll();
Iterable<T> findAllById(Iterable<ID> ids);
long count();
void deleteById(ID id);
void delete(T entity);
void deleteAllById(Iterable<? extends ID> ids);
void deleteAll(Iterable<? extends T> entities);
void deleteAll();
}
@Query 사용
@QueryAnnotation을 사용하여 Native query를 직접 작성한 메서드를 추가할 수 있다.
public interface TestRepository extends JpaRepository<Test, Long> {
@Query(
value = "SELECT * FROM test WHERE count > ?",
nativeQuery = true
)
List<Test> findTestGreaterThenCount(int count);
}
Spring JPA Connection 관련 설정
application.properties또는application.yml파일에서 JDBC 설정을 추가한다.- 접속할 데이터베이스의 주소, 드라이버 설정, 사용자, 비밀번호 등 연결 관련 설정을 추가한다.
# 애플리케이션 이름
spring.application.name=security
# port 설정
server.port=9000
# MySQL 드라이버 설정
spring.datasource.driver-class-name=db_driver_name
# 연결할 DB 주소. 마지막은 db 이름이나 schema 이름을 입력한다.
spring.datasource.url=jdbc:db_type://localhost:3306/databasename
# DB 사용자 이름 및 비밀번호
spring.datasource.username=username
spring.datasource.password=password
# hibernate ddl 설정 - create면 새로 생성
spring.jpa.hibernate.ddl-auto=create
# sql을 콘솔에 표시할지 여부
spring.jpa.show-sql=true
# Hibernate가 DB에 맞게 SQL을 생성하고 최적화(방언 설정)
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.db_type
# Hibernate가 실행하는 SQL 쿼리를 보기 쉽도록 포맷팅
spring.jpa.properties.hibernate.format_sql=true